package com.vhall.component.service.pay.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.vhall.component.dao.pay.PaymentOrderMapper;
import com.vhall.component.entity.pay.dto.NotifyDTO;
import com.vhall.component.entity.pay.dto.PaymentDTO;
import com.vhall.component.entity.pay.entity.PaymentOrderEntity;
import com.vhall.component.entity.pay.vo.PaymentParamVO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.service.pay.NotifyService;
import com.vhall.component.service.pay.PaymentOrderService;
import com.vhall.component.service.pay.PaymentService;
import com.vhall.component.service.pay.SettingService;
import com.vhall.component.service.pay.constant.Channel;
import com.vhall.component.service.pay.constant.OrderStatus;
import com.vhall.component.service.pay.constant.ServiceCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author xuyongzheng
 * @since 2020/10/22
 **/
@Slf4j
@Service
public class PaymentServiceImpl implements PaymentService {

    @Autowired
    private AliPayService alipay;

    @Autowired
    private WxPayService wxpay;

    @Autowired
    private PaymentOrderMapper paymentOrderMapper;

    @Autowired
    private NotifyService notifyService;

    @Value("${vhall.pay.fake:false}")
    private Boolean ifPay;

    @Value("${vhall.pay.domain}")
    private String payDomain;

    private static final String NOTIFY_URL = "/v4/pay/notify";

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private PaymentOrderService paymentOrderService;

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private SettingService settingService;


    @Override
    public Object build(PaymentOrderEntity entity) {
        if (entity.getChannel().equals(Channel.WEIXIN)) {
            return buildWxPayment(entity);
        }
        if (entity.getChannel().equals(Channel.ALIPAY)) {
            return buildAliPayment(entity);
        }
        return new PaymentDTO();
    }

    public Object buildWxPayment(PaymentOrderEntity entity) {
        if (entity.getServiceCode().equals(ServiceCode.QR_PAY)) {
            return wxpay.qrPayOrder(entity);
        }
        if (entity.getServiceCode().equals(ServiceCode.H5_PAY)) {
            return wxpay.wapPayOrder(entity);
        }
        if (entity.getServiceCode().equals(ServiceCode.JSAPI)) {
            return wxpay.jsApiOrder(entity);
        }
        return "";
    }

    public String buildAliPayment(PaymentOrderEntity entity) {
        if (entity.getServiceCode().equals(ServiceCode.QR_PAY)) {
            return alipay.qrPayOrder(entity);
        }
        if (entity.getServiceCode().equals(ServiceCode.H5_PAY)) {
            return alipay.wapPayOrder(entity);
        }
        if (entity.getServiceCode().equals(ServiceCode.CASHIER)) {
            return alipay.cashierPay(entity);
        }
        return "";
    }

    @Override
    public Boolean payNotify(Integer orderId) {
        PaymentOrderEntity order = paymentOrderMapper.selectById(orderId);
        NotifyDTO vo = new NotifyDTO();
        BeanUtil.copyProperties(order, vo);
        vo.setPayStatus(order.getStatus());
        boolean success = false;
        String response = "";
        try {
            Map<String, Object> params = BeanUtil.beanToMap(vo, true, false);
            response = notifyService.payNotify(params);
            if ("success".equalsIgnoreCase(response.trim())) {
                success = true;
                log.info("调用组件支付通知接口成功,params={}", JSON.toJSONString(params));
            }
        } catch (Exception e) {
            log.error("调用组件支付通知接口异常", e);
            return false;
        }
        if (!success) {
            log.info("正在进行回调重试");
        }
        return success;
    }


    @Override
    public Boolean tradeNoInsertCache(Map<String, Object> data) {
        log.info("交易信息入缓存开始，data:{}", data);
        if (!ifPay || ObjectUtil.isNull(data.get("trade_no"))) {
            return false;
        }
        try {
            LocalDateTime createdAt = (LocalDateTime) data.get("created_at");
            String format = createdAt.format(DatePattern.NORM_DATETIME_FORMATTER);
            data.put("created_at", format);
            redisTemplate.opsForValue().set(RedisKey.TRADE_NO + data.get("trade_no"), JSON.toJSONString(data), 60, TimeUnit.SECONDS);
            log.info("交易信息入缓存成功！");
        } catch (Exception e) {
            log.error("交易信息入缓存失败,message:{}", e.getMessage());
            return false;
        }
        return true;
    }

    @Override
    public Map<String, Object> getTradeNoCache(String tradeNo) {
        if (!ifPay || StringUtils.isBlank(tradeNo)) {
            return null;
        }
        String trade = (String) redisTemplate.opsForValue().get(RedisKey.TRADE_NO + tradeNo);
        if (StringUtils.isAllEmpty(trade)) {
            return null;
        }
        return JSON.parseObject(trade, Map.class);
    }

    @Override
    public Object genPayment(PaymentParamVO reqVO) {
        //0. 联合参数校验
        if (ServiceCode.JSAPI.equals(reqVO.getServiceCode()) && StringUtils.isEmpty(reqVO.getOpenId())) {
            throw new BusinessException(BizErrorCode.COMP_PAY_WX_OPENID_EMPTY);
        }
        //1. 查询订单是否存在
        PaymentOrderEntity order = paymentOrderService.findByBizOrderId(reqVO.getBizOrderNo());
        // 1.1 状态判断
        if (order != null && order.getStatus().equals(OrderStatus.SUCCESS)) {
            throw new BusinessException(BizErrorCode.COMP_PAY_ORDER_PAYED);
        }
        //检查app id
        settingService.mapBy(reqVO.getAppId(), reqVO.getChannel(), reqVO.getServiceCode());
        if (order == null) {
            order = new PaymentOrderEntity();
            order.setAppId(reqVO.getAppId());
            order.setBizOrderNo(reqVO.getBizOrderNo());
            order.setChannel(reqVO.getChannel());
            order.setServiceCode(reqVO.getServiceCode());
            order.setTitle(reqVO.getTitle());
            order.setTotalFee(reqVO.getTotalFee());
            order.setDetail(reqVO.getDetail());
            order.setRemark(reqVO.getRemark());
            order.setNotifyUrl(payDomain + NOTIFY_URL);
            order.setShowUrl(reqVO.getShowUrl());
            order.setOpenId(reqVO.getOpenId());
            order.setOptional(reqVO.getOptional());
            order.setClientIp(reqVO.getClientIp());
            order.setStatus(OrderStatus.WAITING);
            order = paymentOrderService.create(order);
        } else {
            //每次请求过来的IP可能是变化的 所以需要修改
            // 2. 修改订单支付方式
            order.setServiceCode(reqVO.getServiceCode());
            order.setChannel(reqVO.getChannel());
            order.setClientIp(reqVO.getClientIp());
            paymentOrderService.updateById(order);
        }

        // 1.2 金额判断
        if (order.getTotalFee().compareTo(reqVO.getTotalFee()) != 0) {
            throw new BusinessException(BizErrorCode.COMP_PAY_AMOUNT_UNEDITABLE);
        }

        // 如果有open_id，那么需要更新数据库中订单的open_id，
        // 因为在微信jsapi支付方式中需要做open_id更正处理
        if (StringUtils.isNotBlank(reqVO.getOpenId())) {
            paymentOrderService.updateOpenId(reqVO.getBizOrderNo(), reqVO.getOpenId());
            order.setOpenId(reqVO.getOpenId());
        }
        return paymentService.build(order);
    }
}
